#include "sliceiterator.hpp"

SliceIterator::SliceIterator(Slice &slice) {
  first=&slice;
  second=third=NULL;
  atFront=true;
  atBack=false;
  indexCounter=0;
}

SliceIterator::SliceIterator(Slice &first,Slice &second) {
  assert(first.size()==second.size());
  this->first=&first;
  this->second=&second;
  third=NULL;
  atFront=true;
  atBack=false;
  indexCounter=0;
}

SliceIterator::SliceIterator(Slice &first,Slice &second,Slice &third) {
  assert(first.size()==second.size() and first.size()==third.size());
  this->first=&first;
  this->second=&second;
  this->third=&third;
  atFront=true;
  atBack=false;
  indexCounter=0;
}

void SliceIterator::execute(void (* callback) (unsigned char &value,void *param),void *param) {
  assert(first!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),param);
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &value,const Vector3i &index,void *param),void *param) {
  assert(first!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),first->getIndex()-first->getStart(),param);
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second,void *param),void *param) {
  assert(first!=NULL and second!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value(),param);
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second,const Vector3i &index,void *param),void *param) {
  assert(first!=NULL and second!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value(),first->getIndex()-first->getStart(),param);
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second,unsigned char &third,void *param),void *param) {
  assert(first!=NULL and second!=NULL and third!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value(),third->value(),param);
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second,unsigned char &third,const Vector3i &index,void *param),void *param) {
  assert(first!=NULL and second!=NULL and third!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value(),third->value(),first->getIndex()-first->getStart(),param);
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &value)) {
  assert(first!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value());
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &value,const Vector3i &index)) {
  assert(first!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),first->getIndex()-first->getStart());
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second)) {
  assert(first!=NULL and second!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value());
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second,const Vector3i &index)) {
  assert(first!=NULL and second!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value(),first->getIndex()-first->getStart());
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second,unsigned char &third)) {
  assert(first!=NULL and second!=NULL and third!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value(),third->value());
  }
  toFront();
}

void SliceIterator::execute(void (* callback) (unsigned char &first,unsigned char &second,unsigned char &third,const Vector3i &index)) {
  assert(first!=NULL and second!=NULL and third!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(first->value(),second->value(),third->value(),first->getIndex()-first->getStart());
  }
  toFront();
}

void SliceIterator::execute(void (*callback) (Slice &slice,void *param),void *param) {
  assert(first!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(*first,param);
  }
  toFront();
}

void SliceIterator::execute(void (*callback) (Slice &first,Slice &second,void *param),void *param) {
  assert(first!=NULL and second!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(*first,*second,param);
  }
  toFront();
}

void SliceIterator::execute(void (*callback) (Slice &first,Slice &second,Slice &third)) {
  assert(first!=NULL and second!=NULL and third!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(*first,*second,*third);
  }
  toFront();
}

void SliceIterator::execute(void (*callback) (Slice &first,Slice &second,Slice &third,void *param),void *param) {
  assert(first!=NULL and second!=NULL and third!=NULL);
  toFront();
  while (hasNext()) {
    next();
    callback(*first,*second,*third,param);
  }
  toFront();
}

//template <class T>
//void SliceIterator::execute(void (* callback) (unsigned char & value,T &param),T &param) {
//  assert(first!=NULL);
//  toFront();
//  while (hasNext()) {
//    next();
//    callback(first->value(),param);
//  }
//  toFront();
//}

const Vector3i & SliceIterator::getIndex() const {
  return first->getIndex();
}

const int & SliceIterator::x() const {
  return first->x();
}

const int & SliceIterator::y() const {
  return first->y();
}

const int & SliceIterator::z() const {
  return first->z();
}

void SliceIterator::next() {
  assert(!atBack);
  if (atFront) {
    atFront=false;
    if (first->getIndex()+Vector3i(1,1,1)==first->getEnd()) {
      atBack=true;
    }
    return;
  }
  if (indices.empty()) {
    first->x()++;
    if (first->x()>=first->getEnd().x()) {
      first->x()=first->getStart().x();
      first->y()++;
    }
    if (first->y()>=first->getEnd().y()) {
      first->y()=first->getStart().y();
      first->z()++;
    }
    if (first->getIndex()+Vector3i(1,1,1)==first->getEnd()) {
      atBack=true;
    }
  } else {
    indexCounter++;
    first->setIndex(indices.at(indexCounter));
    if (indexCounter==indices.size()-1) {
      atBack=true;
    }
  }
  if (second!=NULL) {
    second->x()=first->x()+first->getOffset().x()-second->getOffset().x();
    second->y()=first->y()+first->getOffset().y()-second->getOffset().y();
    second->z()=first->z()+first->getOffset().z()-second->getOffset().z();
  }
  if (third!=NULL) {
    third->x()=first->x()+first->getOffset().x()-third->getOffset().x();
    third->y()=first->y()+first->getOffset().y()-third->getOffset().y();
    third->z()=first->z()+first->getOffset().z()-third->getOffset().z();
  }
}

unsigned char SliceIterator::value() const {
  assert(first!=NULL);
  return first->value();
}

unsigned char SliceIterator::firstValue() const {
  assert(first!=NULL);
  return first->value();
}

unsigned char SliceIterator::secondValue() const {
  assert(second!=NULL);
  return second->value();
}

unsigned char SliceIterator::thirdValue() const {
  assert(third!=NULL);
  return third->value();
}

bool SliceIterator::hasNext() const {
  return !atBack;
}

void SliceIterator::toFront() {
  indexCounter=0;
  first->x()=first->getStart().x();
  first->y()=first->getStart().y();
  first->z()=first->getStart().z();
  if (second!=NULL) {
    second->x()=second->getStart().x();
    second->y()=second->getStart().y();
    second->z()=second->getStart().z();
  }
  if (third!=NULL) {
    third->x()=third->getStart().x();
    third->y()=third->getStart().y();
    third->z()=third->getStart().z();
  }
  atBack=false;
  atFront=true;
}

void SliceIterator::setIndices(const QList<Vector3i> &indices,const Vector3i &offset) {
  assert(first!=NULL);
  QListIterator<Vector3i> it(indices);
  while (it.hasNext()) {
    Vector3i i=it.next();
    if (((i+offset).array()>=first->getStart().array()).all()
        and ((i+offset).array()<first->getEnd().array()).all()) {
      this->indices.append(i+offset);
    }
  }
  if (indices.size()>0) {
    first->setIndex(indices.at(indexCounter));
    if (second!=NULL) {
      second->setIndex(indices.at(indexCounter));
    }
    if (third!=NULL) {
      third->setIndex(indices.at(indexCounter));
    }
  }
}

void SliceIterator::clearIndices() {
  indices.clear();
}
